﻿package idv.cjcat.fenofsm {
	use namespace ffsm;
	
	import flash.errors.*;
	import flash.events.*;
	
	import idv.cjcat.fenofsm.events.*;
	
	/**
	 * A <code>Transition</code> object represents a transition in a finite state machine.
	 * This class is not meant to be instantiated manually.
	 * Use the <code>FSMachine.createTransition()</code> method instead to create a transition.
	 */
	public class Transition extends EventDispatcher {
		
		/**
		 * The custom data of the state.
		 * <p>
		 * You can assign whatever object you want to this property to get it associated with this transition.
		 * </p>
		 */
		public var data:*;
		/**
		 * The delay time of the transition in seconds.
		 * <p>
		 * When the transition is triggered.
		 * The finite state machine will remain in the origin state for this amount of time.
		 * After the delay, the finite state machine will enter the destination state.
		 * By default, a transition has zero delay time.
		 * </p>
		 * <p>
		 * If the delay time is set to a negative value,
		 * the transition delays indefinitely;
		 * not until the <code>go()</code> method is called will the delay end.
		 * </p>
		 */
		public var delay:Number = 0;
		
		/** @private */
		ffsm var _name:String;
		/** @private */
		ffsm var _from:State;
		/** @private */
		ffsm var _to:State;
		/** @private */
		ffsm var _input:*;
		/** @private */
		ffsm var _fsm:FSMachine;
		/** @private */
		ffsm var _waited:Boolean = false;
		
		public function Transition() {
			
		}
		
		/**
		 * Initializes a transition.
		 * <p>
		 * Do not use this constructor to create a transition object.
		 * Use the <code>FSMachine.createTransition()</code> method instead.
		 * </p>
		 * @param	from          The origin state.
		 * @param	to            The destination state.
		 * @param	input         The input the transition shall respond to.
		 * @param	name          The name of the transition.
		 * @param   delay         Transition delay time.
		 */
		final public function initialize(from:State, to:State, input:*, name:String, delay:Number, fsm:FSMachine):void {
			this._from = from;
			this._to = to; 
			this._input = input;
			this._name = name;
			this.delay = delay;
			this._fsm = fsm;
		}
		
		/**
		 * Ends the indefinite delay in the case of negative delay value.
		 */
		public function go():void {
			if (_waited) {
				this._fsm._currentState = this._to;
				this.dispatchEvent(new TransitionEvent(TransitionEvent.COMPLETE, this));
				this._to.dispatchEvent(new StateEvent(StateEvent.ENTER, this._to));
				this._fsm._frozen = false;
				_waited = false;
			}
		}
		
		/**
		 * The name of the transition.
		 */
		public function get name():String { return _name; }
		/**
		 * The origin state.
		 */
		public function get from():State { return _from; }
		/**
		 * The destination state.
		 */
		public function get to():State { return _to; }
		/**
		 * The input the transition shall respond to.
		 * <p>
		 * The input must be a primitive object.
		 * </p>
		 */
		public function get input():* { return _input; }
		public function set input(value:*):void {
			if (!(value is Number) && !(value is int) && !(value is uint) && !(value is Boolean) && !(value is String)) {
				throw new IllegalOperationError("The input must be a primitive object");
			}
				
			for each (var t:Transition in _from._outTransitions) {
				if (t._input === value) throw new IllegalOperationError("A transition from the origin state already responds to the same input.");
			}
			_input = value;
		}
		
		/**
		 * The finite state machine the transition is in.
		 */
		public function get fsm():FSMachine { return _fsm; }
		
		/**
		 * Override this function to generate custom XML representation.
		 * Altering the <code>from, to, name, input, inputType, delay</code> attributes in the root node 
		 * does not affect the final output from the <code>FSMachine.toXML()</code> method.
		 * @param	xml
		 */
		internal function toXML():XML { return <transition/>; }
		
		/**
		 * Override this function to perform custom XML parsing.
		 * @param	xml
		 */
		internal function parseXML(xml:XML):void { }
		
		override public function toString():String {
			return "[Transition "
					+ "name='" + _name + "' "
					+ "from='" + _from._name + "' "
					+ "to='" + _to._name + "' "
					+ "input='" + _input + "' "
					+ "delay='" + delay
					+ "']";
		}
	}
}